﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.RandomNumberGenerator
{
    /// <summary>
    /// The ExtendedRandom class provides methods for generating random numbers.
    /// </summary>
    [Serializable]
    public class ExtendedRandom : System.Random
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ExtendedRandom"/> class.
        /// </summary>
        public ExtendedRandom()
            : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ExtendedRandom"/> class.
        /// </summary>
        /// <param name="seed">A number used to calculate a starting value for the pseudo-random number sequence. If a negative number is specified, 
        /// the absolute value of the number is used.</param>
        public ExtendedRandom(int seed)
            : base(seed)
        {
        }

        /// <summary>
        /// Generates a random integer array.
        /// </summary>
        /// <param name="items">The number of items of the random array.</param>
        /// <returns>A random array of numbers.</returns>
        public int[] NextIntegerArray(int items)
        {
            return this.NextIntegerArray(items, int.MaxValue);
        }

        /// <summary>
        /// Generates a random integer array.
        /// </summary>
        /// <param name="items">The number of items of the random array.</param>
        /// <param name="maxValue">The maximum value for a random number.</param>
        /// <returns>A random array of numbers.</returns>
        public int[] NextIntegerArray(int items, int maxValue)
        {
            if (items < 0)
            {
                throw new ArgumentException("items < 0", "items");
            }

            int[] result = new int[items];

            for (int i = 0; i < items; i++)
            {
                result[i] = this.Next(maxValue);
            }

            return result;
        }

        /// <summary>
        /// Generates a random double array.
        /// </summary>
        /// <param name="items">The number of items of the random array.</param>
        /// <returns>A random array of numbers.</returns>
        public double[] NextDoubleArray(int items)
        {
            if (items < 0)
            {
                throw new ArgumentException("items < 0", "items");
            }

            double[] result = new double[items];

            for (int i = 0; i < items; i++)
            {
                result[i] = this.NextDouble();
            }

            return result;
        }

        /// <summary>
        /// Generates a random string.
        /// </summary>
        /// <param name="length">The length of the random string.</param>
        /// <returns>The generated random string.</returns>
        public String NextString(int length)
        {
            if (length < 0)
            {
                throw new ArgumentException("length < 0", "length");
            }

            if (length == 0)
            {
                return "";
            }

            String result = "";

            for (int i = 0; i < length; i++)
            {
                char tempuri = (char) this.Next(256);

                result += tempuri;
            }

            return result;
        }

        /// <summary>
        /// Generates a random unsigned integer between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue 
        /// must be greater than or equal to minValue.</param>
        /// <returns>A 32-bit signed integer greater than or equal to minValue and less than 
        /// maxValue; that is, the range of return values includes minValue but not maxValue. 
        /// If minValue equals maxValue, minValue is returned.</returns>
        public uint Next(uint minValue, uint maxValue)
        {
            if (maxValue - minValue == uint.MaxValue)
            {
                throw new ArgumentException("maxValue - minValue == uint.MaxValue");
            }

            return ((uint) this.Next()) % (maxValue - minValue + 1) + minValue;
        }

        /// <summary>
        /// Generates a random complex number between minValue and maxValue boundaries.
        /// </summary>
        /// <returns>
        /// A complex number, which real and imaginary number parts are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public ComplexNumber NextComplexNumber()
        {
            return new ComplexNumber(this.Next(), this.Next());
        }

        /// <summary>
        /// Generates a random complex number between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A complex number, which real and imaginary number parts are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public ComplexNumber NextComplexNumber(int minValue, int maxValue)
        {
            return new ComplexNumber(this.Next(minValue, maxValue), this.Next(minValue, maxValue));
        }

        /// <summary>
        /// Generates a random matrix between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="rows">The number of rows of the random matrix.</param>
        /// <param name="columns">The number of columns of the random matrix.</param>
        /// <returns>
        /// A matrix, which components are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public Matrix NextMatrix(int rows, int columns)
        {
            Matrix result = new Matrix(rows, columns);

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    result.SetValueAtPosition(new MatrixPosition(i, j), this.Next());
                }
            }

            return result;
        }

        /// <summary>
        /// Generates a random matrix between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="rows">The number of rows of the random matrix.</param>
        /// <param name="columns">The number of columns of the random matrix.</param>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A matrix, which components are greater than or equal to minValue and less than 
        /// maxValue; that is, the range of return values includes minValue but not maxValue. 
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public Matrix NextMatrix(int rows, int columns, int minValue, int maxValue)
        {
            Matrix result = new Matrix(rows, columns);

            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    result.SetValueAtPosition(new MatrixPosition(i, j), this.Next(minValue, maxValue));
                }
            }

            return result;
        }

        /// <summary>
        /// Generates a random vector between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="components">The number of components of the random vector.</param>
        /// <returns>
        /// A vector, which components are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public GeneralVector NextVector(int components)
        {
            GeneralVector result = new GeneralVector(components);

            for (int i = 0; i < components; i++)
            {
                result[i] = this.Next();
            }

            return result;
        }

        /// <summary>
        /// Generates a random vector between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="components">The number of components of the random vector.</param>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A vector, which components are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public GeneralVector NextVector(int components, int minValue, int maxValue)
        {
            GeneralVector result = new GeneralVector(components);

            for (int i = 0; i < components; i++)
            {
                result[i] = this.Next(minValue, maxValue);
            }

            return result;
        }

        /// <summary>
        /// Generates a random vector between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="components">The number of components of the random vector.</param>
        /// <returns>
        /// A vector, which components are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public GeneralComplexVector NextComplexVector(int components)
        {
            GeneralComplexVector result = new GeneralComplexVector(components);

            for (int i = 0; i < components; i++)
            {
                result[i] = new ComplexNumber(this.Next(), this.Next()); 
            }

            return result;
        }

        /// <summary>
        /// Generates a random vector between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="components">The number of components of the random vector.</param>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A vector, which components are greater than or
        /// equal to minValue and less than maxValue; that is, the range of return values includes
        /// minValue but not maxValue. If minValue equals maxValue, minValue is returned.
        /// </returns>
        public GeneralComplexVector NextComplexVector(int components, int minValue, int maxValue)
        {
            GeneralComplexVector result = new GeneralComplexVector(components);

            for (int i = 0; i < components; i++)
            {
                result[i] = new ComplexNumber(this.Next(minValue, maxValue), this.Next(minValue, maxValue));
            }

            return result;
        }

        /// <summary>
        /// Generates a random polynomial between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="degree">The degree of the random polynomial.</param>
        /// <returns>
        /// A polynomial, which coefficients are greater than or equal to minValue and less than
        /// maxValue; that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public SimplePolynomial NextSimplePolynomial(int degree)
        {
            SimplePolynomial result = new SimplePolynomial(degree);

            for (int i = 0; i < result.Coefficients.Length; i++)
            {
                result.SetValueAtRank(i, this.Next());
            }

            return result;
        }

        /// <summary>
        /// Generates a random polynomial between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="degree">The degree of the random polynomial.</param>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A polynomial, which coefficients are greater than or equal to minValue and less than
        /// maxValue; that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public SimplePolynomial NextSimplePolynomial(int degree, int minValue, int maxValue)
        {
            SimplePolynomial result = new SimplePolynomial(degree);

            for (int i = 0; i < result.Coefficients.Length; i++)
            {
                result.SetValueAtRank(i, this.Next(minValue, maxValue));
            }

            return result;
        }

        /// <summary>
        /// Generates a random polynomial between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="degree">The degree of the random polynomial.</param>
        /// <returns>
        /// A polynomial, which coefficients are greater than or equal to minValue and less than
        /// maxValue; that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public Polynomial NextPolynomial(int degree)
        {
            return this.NextSimplePolynomial(degree).ToPolynomial();
        }

        /// <summary>
        /// Generates a random polynomial between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="degree">The degree of the random polynomial.</param>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A polynomial, which coefficients are greater than or equal to minValue and less than
        /// maxValue; that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public Polynomial NextPolynomial(int degree, int minValue, int maxValue)
        {
            return this.NextSimplePolynomial(degree, minValue, maxValue).ToPolynomial();
        }

        /// <summary>
        /// Generates a complex random polynomial between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="degree">The degree of the random polynomial.</param>
        /// <returns>
        /// A complex polynomial, which coefficients are greater than or equal to minValue and less than
        /// maxValue; that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public ComplexPolynomial NextComplexPolynomial(int degree)
        {
            ComplexPolynomial result = new ComplexPolynomial(degree + 1);

            for (int i = 0; i < result.Components.Count; i++)
            {
                result.SetComponentAt(i, new ComplexPolynomialComponent
                                             (this.NextComplexNumber()));
            }

            return result;
        }

        /// <summary>
        /// Generates a complex random polynomial between minValue and maxValue boundaries.
        /// </summary>
        /// <param name="degree">The degree of the random polynomial.</param>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue
        /// must be greater than or equal to minValue.</param>
        /// <returns>
        /// A complex polynomial, which coefficients are greater than or equal to minValue and less than
        /// maxValue; that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public ComplexPolynomial NextComplexPolynomial(int degree, int minValue, int maxValue)
        {
            ComplexPolynomial result = new ComplexPolynomial(degree + 1);

            for (int i = 0; i < result.Components.Count; i++)
            {
                result.SetComponentAt(i, new ComplexPolynomialComponent
                                             (this.NextComplexNumber(minValue, maxValue)));
            }

            return result;
        }
    }
}